"""
消息推送
- SSE 实时推送
- REST 轮询未读
- 标记已读
"""
import asyncio
import json
from typing import Dict

from fastapi import APIRouter, Depends
from fastapi.responses import StreamingResponse

from app.common.auth import get_current_user
from app.crud.notification_crud import get_unread, mark_as_read
from app.schemas.user_schemas import UserInDB
from common.database import get_db_dependency
from common.exceptions import BizException, ErrorCode
from common.response import ApiResponse, PaginatedResponse, PaginationParams

router = APIRouter(prefix="/notifications", tags=["Notifications"])

_queues: Dict[int, asyncio.Queue] = {}


@router.get("/sse")
async def sse_notifications(
		current_user: UserInDB = Depends(get_current_user),
) -> StreamingResponse:
	"""建立 SSE 连接，持续推送消息"""
	queue = asyncio.Queue()
	_queues[current_user.id] = queue

	async def event_generator():
		try:
			while True:
				try:
					msg = await asyncio.wait_for(queue.get(), timeout=30)
					yield f"data: {json.dumps(msg)}\n\n"
				except asyncio.TimeoutError:
					yield ":heartbeat\n\n"
		except asyncio.CancelledError:
			_queues.pop(current_user.id, None)

	return StreamingResponse(
		event_generator(),
		media_type="text/event-stream",
		headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
	)


@router.get("", response_model=ApiResponse[PaginatedResponse[dict]])
async def list_notifications(
		params: PaginationParams = Depends(),
		current_user: UserInDB = Depends(get_current_user),
		db=Depends(get_db_dependency),
) -> ApiResponse[PaginatedResponse[dict]]:
	"""获取当前用户未读消息列表"""
	total, items = await get_unread(db, current_user.id, params.page, params.page_size)
	return ApiResponse.success(PaginatedResponse(total=total, items=items))


@router.post("/{notification_id}/read", summary="标记已读")
async def mark_read(
		notification_id: int,
		current_user: UserInDB = Depends(get_current_user),
		db=Depends(get_db_dependency),
) -> ApiResponse[dict]:
	ok = await mark_as_read(db, notification_id, current_user.id)
	if not ok:
		raise BizException.from_error_code(ErrorCode.NOTIFICATION_NOT_FOUND)
	return ApiResponse.success()
